﻿#if NET40 || NET461

using Apewer.Internals.Interop;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace Apewer.Surface
{

    /// <summary></summary>
    internal class FileMapping
    {

        /// <summary>创建内存映射。</summary>
        /// <param name="path">文件路径。</param>
        /// <param name="handle">内存映射句柄。</param>
        /// <param name="block">块大小。</param>
        /// <returns>映射成功。</returns>
        internal bool Create(string path, ref IntPtr handle, ref UInt32 block)
        {
            var DesiredAccess = Constant.GENERIC_READ | Constant.GENERIC_WRITE;
            var ShareMode = FileShare.ReadWrite;
            var CreationDesposition = FileMode.OpenOrCreate;
            var FlagsAndAttributes = Constant.FILE_ATTRIBUTE_NORMAL | Constant.FILE_FLAG_SEQUENTIAL_SCAN;

            if (File.Exists(path))
            {
                try
                {
                    var fh = Kernel32.CreateFile(path, DesiredAccess, ShareMode, IntPtr.Zero, CreationDesposition, FlagsAndAttributes, IntPtr.Zero);
                    if (Constant.INVALID_HANDLE_VALUE != (int)fh)
                    {
                        // 创建内存句柄。
                        var mh = Kernel32.CreateFileMapping(fh, IntPtr.Zero, Constant.PAGE_READWRITE, 0, 0, Guid.NewGuid().ToString());

                        // 获取系统信息。
                        var si = new SystemInfo();
                        Kernel32.GetSystemInfo(ref si);

                        // 得到系统页分配粒度。
                        uint ag = si.dwAllocationGranularity;
                        uint flh = 0;

                        // 获取文件长度。
                        uint fl = Kernel32.GetFileSize(fh, out flh);
                        fl |= (((uint)flh) << 32);

                        // 关闭文件句柄 。
                        Kernel32.CloseHandle(fh);
                        uint bl = 1000 * ag;
                        if (fl < 1000 * ag) bl = fl;

                        // 返回。
                        handle = mh;
                        block = bl;
                        return true;
                    }
                }
                catch { }
            }
            handle = IntPtr.Zero;
            block = 0;
            return false;
        }

        /// <summary>从内存映射中读取数据。</summary>
        /// <param name="handle">内存映射句柄。</param>
        /// <param name="block">块大小。</param>
        /// <param name="offset">读取的位置。</param>
        /// <param name="length">读取的长度。</param>
        /// <returns>读取的结果。</returns>
        internal byte[] Read(IntPtr handle, UInt32 block, Int32 offset, Int32 length)
        {
            var DesiredAccess = (uint)(Constant.FILE_MAP_COPY | Constant.FILE_MAP_READ | Constant.FILE_MAP_WRITE);

            if ((handle != IntPtr.Zero) && (block > 0) && (offset > 0) && (length > 0))
            {
                try
                {
                    // 映射视图，得到地址 。
                    var address = Kernel32.MapViewOfFile(handle, DesiredAccess, (uint)(offset >> 32), (uint)(offset & 0xFFFFFFFF), block);
                    if (address != IntPtr.Zero)
                    {
                        // 从非托管的内存中复制内容到托管的内存中。
                        var start = offset;
                        var buffer = new byte[block];
                        Marshal.Copy(address, buffer, start, (int)length);
                        return buffer;
                    }
                }
                catch { }
            }
            return new byte[0];
        }

        /// <summary>关闭内存映射。</summary>
        /// <param name="handle">内存映射句柄。</param>
        internal static void Close(IntPtr handle)
        {
            if (handle == IntPtr.Zero) return;
            try { Kernel32.CloseHandle(handle); } catch { }
        }

    }

}

#endif
